<?php


namespace app\home\service;

use app\common\model\AgentLavel;
use app\common\model\GameRecords;
use app\common\model\MoneyLog;
use app\common\model\UserModel;
use think\facade\Db;


/**
 * 1 创建定时任务执行写入数据库  mysql_insert_bet_money_log方法
 * 2 开牌以后执行 redis_insert_bet_money_log 方法
 * 转移下注信息到 资金流动表
 * Class TransferBetService
 * @package app\common\service
 */
class TransferBetService
{
    /**
     * @param $info
     * @param $name /redis健名
     * 开牌以后的下注信息 转移 插入到 资金记录表
     * 4 代理计算，丢入redis，计算 redis TransferBetService 执行
     * 5 开牌以后 信息转移到 redis  TransferBetService 执行
     */
    //信息写入 redis
    public function redis_insert_bet_money_log($info, $name = 'redis_insert_bet_money_log'): bool
    {
        if (!$info) return false;
        $result = redis()->RPUSH($name, $info);
        if (!$result) return false;
        return true;
    }

    /**
     * 定时任务 1  执行该方法
     * @param string $name
     * @return array|bool
     * 取出redis下注信息数据。插入资金流动数据库
     */
    public function mysql_insert_bet_money_log($name = 'redis_insert_bet_money_log')
    {
        //1 取出20条数据并删除
        $redis = redis()->LRANGE($name, 0, 20);
        //不存在数据时，直接返回
        if (empty($redis)) return [];
        $moneyLog = [];
        //2 :插入数据库，失败后把数据在插入到 redis
        foreach ($redis as $key => $value) {
            $value = json_decode($value, true);
            $moneyLog[$key]['create_time'] = date('Y-m-d H:i:s');
            $moneyLog[$key]['type'] = $value['delta_amt'] >= 0 ? 1 : 2;
            $moneyLog[$key]['status'] = 501;
            $moneyLog[$key]['money_before'] = $value['before_amt'];
            $moneyLog[$key]['money_end'] = $value['end_amt'];
            $moneyLog[$key]['money'] = $value['delta_amt'];
            $moneyLog[$key]['uid'] = $value['user_id'];
            $moneyLog[$key]['source_id'] = $value['id'];
            $moneyLog[$key]['market_uid'] = 0;
            $moneyLog[$key]['mark'] = '下注金额:' . $value['bet_amt'] . ',变化金额:' . $value['delta_amt'] . '游戏结果:' . $value['result'] . ',下注前:' . $value['before_amt'];
        }
        $log = new MoneyLog();

        $insert = $log->insertAll($moneyLog);
        if (!$insert) return false;
        //成功之后删除指定的 value 值
        $this->del_redis($redis, $name);
        return true;
    }

    /**
     * 定时任务 2
     * 半小时执行一次，避免数据太多
     * 计算用户洗码金额，免佣 : bool
     * @return bool
     */
    public function user_xima_money_settle(): bool
    {
        //查询数据库前一个小时产生的订单..条件.代理没结算，牌面 已结算 ，不是免佣 0 。。因为开了免佣的 没有代理结算没有洗码费
        //查询的都是 用户没开免佣的，并且代理都没结算。就可以计算 洗码费.洗码费大于 0 的。
        $records = GameRecords::alias('a')
            ->whereTime('a.created_at', '-2 hours')
            ->field('a.id,a.shuffling_amt,a.user_id,b.id u_id,b.agent_id')
            ->where(['a.agent_status' => 0, 'a.close_status' => 2, 'a.is_exempt' => 0,'b.is_fictitious'=>0])
            ->where('a.shuffling_amt', '>', 0)
            ->leftJoin('common_user b', 'b.id=a.user_id')
            ->select()
            ->toArray();

        if (empty($records)) return false;

        ##############计算洗码开始##############
        //组装用户洗码数据。
        $user_data = [];//对用户数据过滤去重，并把洗码费加到一起。得到每个用户应该得到的洗码费
        $records_status = [];//保留订单里 需要修改的每条数据的 id，把 洗码状态改为 1 已结算
        foreach ($records as $key => $v) {
            $records_status[$key]['agent_status'] = 1;
            $records_status[$key]['id'] = $v['id'];
            //如果存在说明用户购买了多条,多数据进行去重并把 洗码费加到一起
            if (array_key_exists($v['user_id'], $user_data)) {
                $user_data[$v['user_id']]['shuffling_amt'] += $v['shuffling_amt'];
            } else {
                $user_data[$v['user_id']] = $v; //应为这里做了 key值，所有上面能够判断
            }
        }
        //删除多余的值
        unset($records);


        $agent_data = [];//对用户金额进行组装，有代理的。把洗码费返给代理，把ID换为代理的ID。没代理的。用自己ID
        foreach ($user_data as $key => $v) {
            if ($v['agent_id'] > 0) {
                $agent_data[$key]['id'] = $v['agent_id'];
                $agent_data[$key]['money_balance'] = $v['shuffling_amt'];
                continue;
            }
            $agent_data[$key]['id'] = $v['user_id'];
            $agent_data[$key]['money_balance'] = $v['shuffling_amt'];
        }
        unset($user_data);

        //对插入金额进行最后的去重，每个用户或者代理应该得到多少金额计算出来。
        //应为可能存在   ID多条数据，这里进行最后的去重组装余额
        $data = [];
        foreach ($agent_data as $key => $v) {
            //如果存在说明用户购买了多条,多数据进行去重并把 洗码费加到一起
            if (array_key_exists($v['id'], $data)) {
                $data[$v['id']]['money_balance'] += $v['money_balance'];
            } else {
                //应为这里做了 key值，所有上面能够判断
                $data[$v['id']] = $v;
            }
        }

        ##############计算洗码结束###下面开始写入洗码数据和资金流动日志###########

        //修改数据库的数据,1 修改用户数据 $data，2 修改下注的 洗码结算状态 $records_status
        //只有 id,money_balance
        $save = false;
        $mark = implode(',', array_column($records_status, 'id'));
        // 启动事务
        Db::startTrans();
        try {
            //修改用户余额
            foreach ($data as $key => $value) {
                //修改用户的 洗码免佣额。用户的洗码费 增加到用户余额
                UserModel::where('id', $value['id'])->inc('money_balance', $value['money_balance'])->update();
                //写入资金记录
                $user = UserModel::where('id', $value['id'])->find();
                MoneyLog::insert([
                    'create_time' => date('y-m-d H:i:s'), 'type' => 1, 'status' => 502,
                    'money_before' => $user['money_balance'], 'money_end' => $user['money_balance'] + $value['money_balance'],
                    'money' => $value['money_balance'], 'uid' => $value['id'], 'mark' => '结算洗码费批号:' . $mark
                ]);
            }
            (new GameRecords())->saveAll($records_status);
            //修改用户余额
            // 提交事务
            $save = true;
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            show([], config('ToConfig.http_code.error'), $e->getMessage());
        }
        if ($save) show();
        show([], config('ToConfig.http_code.error'), '洗码结算失败');
    }


    /**
     * 定时任务 3 代理返利 必须在 定时任务 2后面执行
     * @return bool
     */
    public function user_agent_money_settle(): bool
    {
        //查询该结算的数据 只查询结算了洗码的  并且是开牌状态，  和 没开免佣的
        //agent_status =0 直接结算数据， =1 是结算了洗码的
        $orderModel = new GameRecords();
        $records = $orderModel->alias('a')
            ->field('user_id,bet_amt,agent_id,b.id u_id,a.id,type,is_fictitious,money_balance')
            ->where(['a.agent_status' => 0, 'a.close_status' => 2, 'a.is_exempt' => 0,'b.is_fictitious'=>0])
            ->leftJoin('common_user b', 'b.id=a.user_id')
            ->select()->toArray();

        if (empty($records)) return false;

        ###第一步   保留需要修改的订单，去重当前订单用户数据
        //注意上面的不能用 聚合查询
        $user_data = [];//对用户数据过滤去重，并把下注费加到一起。得到每个用户de 下注费
        $records_status = [];//保留订单里 需要修改的每条数据的 id，把 洗码状态改为   9 全部结算
        foreach ($records as $key => $v) {
            $records_status[$key]['agent_status'] = 9;
            $records_status[$key]['id'] = $v['id'];
            //如果存在说明用户购买了多条,多数据进行去重并把 下注费加到一起
            if (array_key_exists($v['user_id'], $user_data)) {
                $user_data[$v['user_id']]['bet_amt'] += $v['bet_amt'];
            } else {
                $user_data[$v['user_id']] = $v; //应为这里做了 key值，所有上面能够判断
            }
        }
        //删除多余的值
        unset($records);

        // 1 查询上一级是否是代理
        // 2 代理返佣。。。比例 * 当前下注金额
        $AgentLavel = new AgentLavel();
        $UserModel = new UserModel();

        $user_member_count_data = [];//存放 获得 返佣的用户信息
        $i = 0;
        //代理下注金额返利
        foreach ($user_data as $key => $value) {
            if ($value['is_fictitious'] == 1) continue;
            if ($value['agent_id'] <= 0) continue;
            //特殊 是会员的时候 的返利 需返给当前的第一个代理
            if ($value['type'] != 1) {
                //查询当前订单是会员的时候，上级是代理的用户
                if ($value['agent_id'] == 0) continue;
                $user = $this->agent_user($UserModel, $value['agent_id']);
                if (empty($user)) continue;
                //存在上级是代理的时候，该代理获得返佣，并且该代理的所有上级都获得
                $agent_rate = floatval($user['agent_rate']);
                if ($agent_rate <= 0) continue;
                // 2 代理返佣。。。比例 * 当前下注金额
                $user_member_count_data[$i]['money'] = $agent_rate * $value['bet_amt'] / 100;//应该返的佣金
                $user_member_count_data[$i]['id'] = $user['id'];//获得佣金的用户
                $user_member_count_data[$i]['trigger_id'] = $value['user_id'];//谁触发返佣机制
                $user_member_count_data[$i]['bet_money'] = $value['bet_amt'];//总分配的金额
                if (!isset($user_member_count_data[$i]['total_money'])) $user_member_count_data[$i]['total_money'] = 0;
                $user_member_count_data[$i]['total_money'] += $agent_rate * $value['bet_amt'] / 100;//总分配出去的金额
                $user_member_count_data[$i]['money_balance'] = $value['money_balance'];
                $i++;
                $value['user_id'] = $user['id'];
                unset($user);
            }
            //查询当前会员代理的代理分销,先返给当前会员的代理。在进行下面的代理返返利
            //获得代理的所有上级代理。这个表不存在的人，就不是代理
            $agentList = $AgentLavel->where('agent_id', $value['user_id'])->select()->toArray();
            if (empty($agentList)) continue;

            //循环该用户的所有上级 ，计算所有上级应该获得的金额
            foreach ($agentList as $k => $v) {
                //查询上级的分销比例
                $agent_rate = $UserModel->where('id', $v['agent_pid'])->value('agent_rate');
                $agent_rate = floatval($agent_rate);
                if ($agent_rate <= 0) continue;
                // 2 代理返佣。。。比例 * 当前下注金额
                $user_member_count_data[$i]['money'] = $agent_rate * $value['bet_amt'] / 100;//应该返的佣金
                $user_member_count_data[$i]['id'] = $v['agent_pid'];//获得佣金的用户
                $user_member_count_data[$i]['trigger_id'] = $value['u_id'];//谁触发返佣机制
                $user_member_count_data[$i]['bet_money'] = $value['bet_amt'];//总分配的金额
                if (!isset($user_member_count_data[$i]['total_money'])) $user_member_count_data[$i]['total_money'] = 0;
                $user_member_count_data[$i]['total_money'] += $agent_rate * $value['bet_amt'] / 100;//总分配出去的金额
                $user_member_count_data[$i]['money_balance'] = $value['money_balance'];
                $i++;
            }
        }

        //如果存在说明用户购买了多条,多数据进行去重并把 洗码费加到一起
        $user_count_data = [];
        foreach ($user_member_count_data as $key => $v) {
            if (array_key_exists($v['id'], $user_count_data)) {
                $user_count_data[$v['id']]['money'] += $v['money'];
                $user_count_data[$v['id']]['total_money'] += $v['total_money'];
                $user_count_data[$v['id']]['bet_money'] += $v['bet_money'];
                $user_count_data[$v['id']]['trigger_id'] .= ',' . $v['trigger_id'];
            } else {
                $user_count_data[$v['id']] = $v;
            }
        }
        // 启动事务
        Db::startTrans();
        try {
            // 1修改 订单代理结算状态
            $orderModel->saveAll($records_status);
            // 2增加代理 余额
            foreach ($user_count_data as $key=>$value){
                //查询当前用户的余额
                $user = $UserModel->where('id', $value['id'])->find();
                $user->save(['money_balance'=>$user->money_balance + $value['money']]);
                // 3写入资金记录
                (new MoneyLog())->save([
                    'create_time'=>date('Y-m-d H:i:s'),
                    'type'=>1,
                    'money_before'=>$user->money_balance,
                    'money_end'=>$user->money_balance + $value['money'],
                    'money'=>$value['id'],
                    'source_id'=>0,
                    'market_uid'=>0,
                    'mark'=>'代理返佣：'.$value['money'].',trigger_id:'.$value['trigger_id']
                ]);
            }
            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            return false;
        }
        return true;
    }

    //查询 会员的上一级代理 $pid 上级代理
    public function agent_user($UserModel, $pid): array
    {
        if ($pid <= 0) return [];
        $user = $UserModel->where('id', $pid)->find();
        if (empty($user)) return [];
        $user = $user->toArray();
        if ($user['type'] != 1) {
            return $this->agent_user($UserModel, $user['agent_id']);
        }
        return $user;
    }

    //删除redis值
    public function del_redis($redis, $name)
    {
        foreach ($redis as $key => $value) {
            redis()->LREM($name, $value);//删除当前已经计算过的值
        }
    }
}
